import { type InjectionKey, provide, inject, ref, reactive, toRefs } from 'vue';
import type { ValidationContext, ValidatorRule, FieldState } from '@/types/validation';

// 1. 管理多表单的 InjectionKey（避免冲突）
const validationContextKeys = new Map<string, InjectionKey<ValidationContext>>();

// 2. 获取当前表单的唯一 InjectionKey（不存在则创建）
const getValidationContextKey = (formKey: string): InjectionKey<ValidationContext> => {
  if (!validationContextKeys.has(formKey)) {
    validationContextKeys.set(
      formKey,
      Symbol(`validation-context-${formKey}`) as InjectionKey<ValidationContext>
    );
  }
  return validationContextKeys.get(formKey)!;
};

// 3. 父表单钩子：提供校验上下文（provide）
export const useValidationProvide = (formKey: string): ValidationContext => {
  // 响应式存储所有表单项状态
  const fields = reactive<Record<string, FieldState>>({});
  // 响应式存储失败的表单项
  const failedFields = ref<FieldState[]>([]);

  // 添加表单项的校验规则
  const addFieldValidator = (field: string, rules: ValidatorRule[], element?: HTMLElement) => {
    // 初始化表单项状态（若不存在）
    if (!fields[field]) {
      fields[field] = {
        field,
        invalid: false,
        errors: [],
        element
      };
    }
    // 扩展校验规则（支持动态添加）
    fields[field].rules = rules;
  };

  // 校验单个表单项
  const validateField = (field: string, value: string): FieldState => {
    const fieldState = fields[field];
    if (!fieldState || !fieldState.rules) return fieldState;

    // 执行所有规则校验，收集错误
    const errors = fieldState.rules
      .filter((rule) => !rule.validator(value, rule.param))
      .map((rule) => rule.message);

    // 更新表单项状态
    fieldState.invalid = errors.length > 0;
    fieldState.errors = errors;
    // 更新失败列表
    updateFailedFields();

    return fieldState;
  };

  // 校验所有表单项
  const validateAll = (): boolean => {
    Object.values(fields).forEach((field) => {
      if (field.rules && field.element) {
        // 获取输入框当前值（从 DOM 中获取，避免 v-model 同步问题）
        const value = (field.element as HTMLInputElement).value;
        validateField(field.field, value);
      }
    });
    // 若有失败项，滚动到第一个错误
    if (failedFields.value.length > 0) {
      scrollToFirstError();
      return false;
    }
    return true;
  };

  // 滚动到第一个错误表单项
  const scrollToFirstError = () => {
    if (failedFields.value[0]?.element) {
      failedFields.value[0].element.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
      // 聚焦到错误输入框
      failedFields.value[0].element.focus();
    }
  };

  // 重置所有校验状态
  const resetValidation = () => {
    Object.values(fields).forEach((field) => {
      field.invalid = false;
      field.errors = [];
      if (field.element) {
        (field.element as HTMLInputElement).value = '';
      }
    });
    failedFields.value = [];
  };

  // 更新失败表单项列表（过滤有效失败项）
  const updateFailedFields = () => {
    failedFields.value = Object.values(fields).filter((field) => field.invalid);
  };

  // 构建校验上下文
  const validationContext: ValidationContext = {
    fields: toRefs(fields) as unknown as Record<string, FieldState>, // 转为普通对象（避免响应式嵌套问题）
    failedFields: failedFields.value,
    addFieldValidator,
    validateField,
    validateAll,
    scrollToFirstError,
    resetValidation
  };

  // 提供上下文给子组件
  const key = getValidationContextKey(formKey);
  provide(key, validationContext);

  return validationContext;
};

// 4. 子组件钩子：注入校验上下文（inject）
export const useValidationInject = (formKey: string): ValidationContext => {
  const key = getValidationContextKey(formKey);
  const context = inject(key);

  // 若未找到上下文，抛出错误（确保子组件在父表单内使用）
  if (!context) {
    throw new Error(
      `[useValidationInject] 未找到表单 "${formKey}" 的校验上下文，请确保子组件在 CustomForm 内使用`
    );
  }

  return context;
};
